/**
  ******************************************************************************
  * @file    dev_w25qxx.h
  * @author  Yun
  * @version V1.0.0
  * @date    2025-02-21
  * @brief   W25QXX设备驱动模块头文件
  * @license EULA (End User License Agreement)
  *          Copyright (c) 2025 星瑜科技自动化 Co., Ltd. All rights reserved.
  ******************************************************************************
  */

#include "dev_w25qxx.h"

#define ENABLE_W25QXX_CS CHECK_DEV_STATUS(Dev_W25Q_ControlCS(pstDevW25QCfg, 0))
#define DISABLE_W25QXX_CS CHECK_DEV_STATUS(Dev_W25Q_ControlCS(pstDevW25QCfg, 1))

/*---------------------- 静态函数 ----------------------*/
/**
  * @brief  控制片选信号
  * @param  pstConfig 设备配置指针
  * @param  u8State 片选状态 (0:使能, 1:禁用)
  * @retval 操作状态
  */
static uint8_t Dev_W25Q_ControlCS(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                                  uint8_t u8State)
{
    return pstDevW25QCfg->pstDevW25QInterface->ControlCS(pstDevW25QCfg->pHwCfg,
                                                         u8State);
}


/**
 * @brief  写使能
 * @param  pstDevW25QCfg 设备配置
 * @return DEV_Status_TypeDef 操作状态码
 */
static uint8_t Dev_W25Q_Write_Enable(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg)
{
    uint8_t u8Cmd = W25Q_CMD_WRITE_ENABLE;

    /* 写使能指令 */
    ENABLE_W25QXX_CS;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                  &u8Cmd,
                                                                  1,
                                                                  W25Q_TIMEOUT));
    DISABLE_W25QXX_CS;

    return DEV_OK;
}

/**
 * @brief  等待准备
 * @param  pstDevW25QCfg 设备配置
 * @return DEV_Status_TypeDef 操作状态码
 */
static uint8_t Dev_W25Q_Wait_Ready(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg)
{
    uint8_t u8Status;
    uint32_t u32TimeOut = 10000000; // 超时计数器
    uint8_t u8Cmd = W25Q_CMD_WAIT_READY;

    /* 等待准备指令 */
    ENABLE_W25QXX_CS;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                  &u8Cmd,
                                                                  1,
                                                                  W25Q_TIMEOUT));
    
    u8Cmd = W25Q_CMD_FULL;
    do {
        CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->TransmitReceive(pstDevW25QCfg->pHwCfg,
                                                                             &u8Cmd,
                                                                             &u8Status,
                                                                             1,
                                                                             W25Q_TIMEOUT));
        if(--u32TimeOut == 0) return DEV_ERROR;
    } while(u8Status & 0x01);
    DISABLE_W25QXX_CS;

    return DEV_OK;
}

/*---------------------- 函数实现 ----------------------*/
/**
 * @brief  初始化W25QXX设备
 */
uint8_t Dev_W25Q_Register(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                          void* pHwCfg,
                          Spi_Interface_TypeDef * pstDevW25QInterface)
{
    /* 参数检查 */
    if(!pstDevW25QCfg || !pHwCfg || !pstDevW25QInterface)
        return DEV_ERROR;

    DEV_UN_INIT(pstDevW25QCfg);

    /* 初始化设备配置 */
    pstDevW25QCfg->pHwCfg = pHwCfg;
    pstDevW25QCfg->pstDevW25QInterface = pstDevW25QInterface;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Init(pHwCfg));

    DEV_INIT(pstDevW25QCfg);

    return DEV_OK;
}

/*!
 * @brief 读取芯片ID
 */
uint8_t Dev_W25Q_ReadID(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                        uint16_t* u16ChipID)
{
    /* 参数检查 */
    if(!pstDevW25QCfg || !u16ChipID)
        return DEV_ERROR;

    CHECK_DEV_ENABLE(pstDevW25QCfg);
    DEV_BUSY(pstDevW25QCfg);

    /* 读取ID */
    uint8_t u8SendData[4] = {W25Q_CMD_READ_ID, W25Q_CMD_NULL, W25Q_CMD_NULL, W25Q_CMD_NULL};
    uint8_t u8Cmd = W25Q_CMD_FULL;
    ENABLE_W25QXX_CS;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                  u8SendData,
                                                                  4,
                                                                  W25Q_TIMEOUT));
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->TransmitReceive(pstDevW25QCfg->pHwCfg,
                                                                         &u8Cmd,
                                                                         (uint8_t*)u16ChipID,
                                                                         1,
                                                                         W25Q_TIMEOUT));
    *u16ChipID <<= 8;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->TransmitReceive(pstDevW25QCfg->pHwCfg,
                                                                         &u8Cmd,
                                                                         (uint8_t*)u16ChipID,
                                                                         1,
                                                                         W25Q_TIMEOUT));
    DISABLE_W25QXX_CS;

    return DEV_OK;
}

/*!
 * @brief 读取数据
 */
uint8_t Dev_W25Q_ReadData(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                          uint32_t u32Addr,
                          uint8_t *pu8Data,
                          uint16_t u16Len)
{
    /* 参数检查 */
    if(!pstDevW25QCfg || !pu8Data ||
       (u32Addr + u16Len) > pstDevW25QCfg->u32FlashSize)
        return DEV_ERROR;

    CHECK_DEV_ENABLE(pstDevW25QCfg);
    DEV_BUSY(pstDevW25QCfg);

    /* 读取数据 */
    uint8_t u8SendData[4] = {W25Q_CMD_READ_DATA, (uint8_t)(u32Addr >> 16), (uint8_t)(u32Addr >> 8), (uint8_t)(u32Addr >> 0)};
    uint8_t u8Cmd = W25Q_CMD_FULL;
    ENABLE_W25QXX_CS;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                  u8SendData,
                                                                  4,
                                                                  W25Q_TIMEOUT));
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->TransmitReceive(pstDevW25QCfg->pHwCfg,
                                                                         &u8Cmd,
                                                                         pu8Data,
                                                                         u16Len,
                                                                         W25Q_TIMEOUT));
    DISABLE_W25QXX_CS;

    return DEV_OK;
}

uint8_t Dev_W25Q_EraseSector(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                             uint32_t u32Sector)
{
    /* 参数检查 */
    if(!pstDevW25QCfg) return DEV_ERROR;

    CHECK_DEV_ENABLE(pstDevW25QCfg);
    DEV_BUSY(pstDevW25QCfg);

    /* 扇区擦除 */
    u32Sector = u32Sector * W25Q_SECTOR_SIZE;
    uint8_t u8SendData[4] = {W25Q_CMD_SECTOR_ERASE,
                             (uint8_t)(u32Sector >> 16),
                             (uint8_t)(u32Sector >> 8),
                             (uint8_t)(u32Sector >> 0)};
    CHECK_DEV_STATUS(Dev_W25Q_Write_Enable(pstDevW25QCfg));
    CHECK_DEV_STATUS(Dev_W25Q_Wait_Ready(pstDevW25QCfg));
    ENABLE_W25QXX_CS;
    CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                  u8SendData,
                                                                  4,
                                                                  W25Q_TIMEOUT));
    DISABLE_W25QXX_CS;
    CHECK_DEV_STATUS(Dev_W25Q_Wait_Ready(pstDevW25QCfg));

    return DEV_OK;
}

uint8_t Dev_W25Q_Write_Sector(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                              uint32_t u32Sector,
                              uint8_t *pu8Data)
{
    /* 参数检查 */
    if(!pstDevW25QCfg || !pu8Data)
        return DEV_ERROR;

    CHECK_DEV_ENABLE(pstDevW25QCfg);
    DEV_BUSY(pstDevW25QCfg);

    u32Sector = u32Sector * W25Q_SECTOR_SIZE;
    uint8_t* pu8Buf = pu8Data;

    for (uint32_t i = 0; i < W25Q_PAGE_NUM; i++)
    {
        uint8_t u8SendData[] = {W25Q_CMD_PAGE_PROGRAM,
                                (uint8_t)(u32Sector >> 16),
                                (uint8_t)(u32Sector >> 8),
                                (uint8_t)(u32Sector >> 0)};
        CHECK_DEV_STATUS(Dev_W25Q_Write_Enable(pstDevW25QCfg));
        CHECK_DEV_STATUS(Dev_W25Q_Wait_Ready(pstDevW25QCfg));
        ENABLE_W25QXX_CS;
        CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                      u8SendData,
                                                                      sizeof(u8SendData),
                                                                      W25Q_TIMEOUT));
        CHECK_DEV_STATUS(pstDevW25QCfg->pstDevW25QInterface->Transmit(pstDevW25QCfg->pHwCfg,
                                                                      pu8Buf,
                                                                      W25Q_PAGE_SIZE,
                                                                      W25Q_TIMEOUT));
        DISABLE_W25QXX_CS;
        CHECK_DEV_STATUS(Dev_W25Q_Wait_Ready(pstDevW25QCfg));

        pu8Buf  += W25Q_PAGE_SIZE;
        u32Sector += W25Q_PAGE_SIZE ;
    }

    return DEV_OK;
}

uint8_t Dev_W25Q_WriteData(Dev_W25Q_Cfg_TypeDef* pstDevW25QCfg,
                           uint32_t u32Addr,
                           uint8_t *pu8Data,
                           uint16_t u16Len)
{
    CHECK_DEV_ENABLE(pstDevW25QCfg);
    DEV_BUSY(pstDevW25QCfg);

    /* 参数检查 */
    if(!pstDevW25QCfg || !pu8Data ||
      (u32Addr + u16Len) > pstDevW25QCfg->u32FlashSize)
        return DEV_ERROR;

    uint8_t* pu8Buf = pu8Data;
    uint32_t  u32SecPos      = u32Addr / W25Q_SECTOR_SIZE;
    uint16_t  u32SecOff      = u32Addr % W25Q_SECTOR_SIZE;
    uint16_t  u32SecRemain   = W25Q_SECTOR_SIZE - u32SecOff;

    if (u16Len <= u32SecRemain) u32SecRemain = u16Len;

    while (1){
        CHECK_DEV_STATUS(Dev_W25Q_ReadData(pstDevW25QCfg,
                          u32SecPos * W25Q_SECTOR_SIZE,
                          pstDevW25QCfg->u8SectorCache,
                          W25Q_SECTOR_SIZE));
        CHECK_DEV_STATUS(Dev_W25Q_EraseSector(pstDevW25QCfg, u32SecPos));

        for (uint16_t i = 0; i < u32SecRemain ; i++)
            pstDevW25QCfg->u8SectorCache[u32SecOff + i] = pu8Buf[i];

        CHECK_DEV_STATUS(Dev_W25Q_Write_Sector(pstDevW25QCfg,
                                               u32SecPos ,
                                               pstDevW25QCfg->u8SectorCache));

        if (u32SecRemain == u16Len) break;
        else{
            pu8Buf = pu8Buf + u32SecRemain ;
            u32SecPos ++;
            u32SecOff = 0;
            u16Len = u16Len - u32SecRemain ;
            u32SecRemain = (u16Len > W25Q_SECTOR_SIZE) ? W25Q_SECTOR_SIZE : u16Len;
        }
    }

    return DEV_OK;
}
